﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Reflection;

namespace kopan_irc
{
    public partial class formTororo : Form
    {
        IRC irc;
        Trans trans = new Trans();
        KeysConverter kc = new KeysConverter();
        KeyPressType TypeNow = KeyPressType.First;

        delegate void FuncDelegate(String str);

        public void AddToText(String str)
        {
            if (this.rTextBoxOut.InvokeRequired)
            {
                FuncDelegate d = new FuncDelegate(AddToText);
                this.Invoke(d, new object[] { str });
            }
            else
            {
                rTextBoxOut.AppendText(str + "\r\n");
                if (toolStripCheckScroll.Checked)
                {
                    rTextBoxOut.Select(rTextBoxOut.Text.Length, 0);
                    rTextBoxOut.ScrollToCaret();
                }
            }
        }

        public void AddToUserList(String str)
        {
            if (this.listBoxUserList.InvokeRequired)
            {
                FuncDelegate d = new FuncDelegate(AddToUserList);
                this.Invoke(d, new object[] { str });
            }
            else
            {
                if (listBoxUserList.Items.Contains(str) == false)
                {
                    listBoxUserList.Items.Add(str);
                }
            }
        }

        public void RemoveToUserList(string str)
        {
            if (this.listBoxUserList.InvokeRequired)
            {
                FuncDelegate d = new FuncDelegate(RemoveToUserList);
                this.Invoke(d, new object[] { str });
            }
            else
            {
                if (listBoxUserList.Items.Contains(str) )
                {
                    listBoxUserList.Items.Remove(str);
                }
            }
        }

        public void RemoveAllUserList()
        {
            listBoxUserList.Items.Clear();
        }

        //public void AddToText(string value, bool enter = true)
        //{
        //    if (enter)
        //    {
        //        rTextBoxOut.Text += value + "\r\n";
        //    }
        //    else
        //    {
        //        rTextBoxOut.Text += value;
        //    }
        //}

        String KeycodeToChar(int keyCode)
        {
            Keys key = (Keys)keyCode;

            switch (key)
            {
                case Keys.Add:
                    return "+";
                case Keys.Decimal:
                    return ".";
                case Keys.Divide:
                    return "/";
                case Keys.Multiply:
                    return "*";
                case Keys.OemBackslash:
                    return "\\";
                case Keys.OemCloseBrackets:
                    return "]";
                case Keys.OemMinus:
                    return "-";
                case Keys.OemOpenBrackets:
                    return "[";
                case Keys.OemPeriod:
                    return ".";
                case Keys.OemPipe:
                    return "|";
                case Keys.OemQuestion:
                    return "?";
                case Keys.OemQuotes:
                    return "\"";
                case Keys.OemSemicolon:
                    return ";";
                case Keys.Oemcomma:
                    return ",";
                case Keys.Oemplus:
                    return "+";
                case Keys.Oemtilde:
                    return "`";
                case Keys.Separator:
                    return "-";
                case Keys.Subtract:
                    return "-";
               
                case Keys.Space:
                    return "  ";
                case Keys.Tab:
                    return "";
                case Keys.Alt:
                    return "";
                case Keys.LShiftKey:
                    return "";
                case Keys.RShiftKey:
                    return "";
                case Keys.ShiftKey:
                    return "";
                case Keys.KanaMode:
                    return "";
                case Keys.Enter:
                    if (TypeNow == KeyPressType.First || TypeNow == KeyPressType.Wait)
                    {
                        TypeNow = KeyPressType.Start;
                       
                    }
                    else if (TypeNow == KeyPressType.Typing)
                    {
                        TypeNow = KeyPressType.End;
                        
                    }
                    return "";
                default:
                    return null;
            }
        }

        public enum KeyPressType
        {
            First = 0,
            End,
            Start,
            Typing,
            Wait,
        }

        [Flags]
        public enum AsyncKeyState : short
        {
            CurrentlyPressed = short.MinValue,
            HasNotBeenPressed = 0,
            HasBeenPressed
        }

        [DllImport("User32.dll")]
        private static extern AsyncKeyState GetAsyncKeyState(Keys key);

        const int WM_PASTE = 0x0302;
        const int WM_KEYDOWN = 0x0100;
        const int VK_RETURN = 0x000D;
        int FindWindowHandle;
        int FindWindowExHandle;

        [DllImport("user32.dll")]
        public static extern int FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll")]
        public static extern int FindWindowEx(int hWnd1, int hWnd2, string lpsz1, string lpsz2);

        [DllImport("user32.dll")]
        public static extern int SendMessage(int hwnd, int wMsg, int wParam, int lParam);

        //Win32 API 
        [DllImport("user32.dll")]
        public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
        [DllImport("user32.dll")]
        public static extern void BringWindowToTop(IntPtr hWnd);
        [DllImport("user32.dll")]
        public static extern void SetForegroundWindow(IntPtr hWnd);
        [DllImport(@"user32.dll", CharSet = CharSet.Auto)]
        private static extern IntPtr GetForegroundWindow();

        IntPtr BeforeHandle;

        //1. 후킹할 이벤트를 등록한다.
        event KeyboardHooker.HookedKeyboardUserEventHandler HookedKeyboardNofity;

        //2 .이벤트가 발생하면 호출된다.
        private long Form1_HookedKeyboardNofity(int iKeyWhatHappened, int vkCode)
        {
            //일단은 기본적으로 키 이벤트를 흘려보내기 위해서 0으로 세팅
            long lResult = 0;

            /////////////////////////////////////////////////////////////////////////////////
            //
            // Hook은 디버그모드에서 잡을 수 없기 때문에
            // 디버그용으로 다음의 코드를 사용한다.
            //textBox1.Text += Environment.NewLine + "iKeyWhatHappened=" + iKeyWhatHappened.ToString();
            //textBox1.Text += Environment.NewLine + "vkCode=" + vkCode.ToString();
            //
            /////////////////////////////////////////////////////////////////////////////////
            //
            // 키 조합에 의해 키가 눌리는 순간 동시에 눌린것을 확인 후 조치를 취한다.
            // L-Alt의 KeyDown: iKeyWhatHappened=32
            // L-Alt의 KeyUp: iKeyWhatHappened=160
            // 후킹은 했지만 키 이벤트는 얌전히 보내준다.
            // 만약 ALT+1에 대한 키 이벤트를 현재 활성화된 윈도우에 보내고싶지 않으면
            // 아래if문들의 lResult값을 모두 1을 주도록 하자.
            //
            /////////////////////////////////////////////////////////////////////////////////
            
            

            string temp = KeycodeToChar(vkCode);
            if (temp == "|")
            {
                BeforeHandle = GetForegroundWindow();
                ShowWindow(this.Handle, 1);
                BringWindowToTop(this.Handle);
                SetForegroundWindow(this.Handle);

                lResult = 1;
            }

            //if (TypeNow == KeyPressType.Start)
            //{
            //    textBox1.Text = "";
            //    TypeNow = KeyPressType.Typing;
            //    //TypeNow = KeyPressType.Start;
            //}

            //if (TypeNow == KeyPressType.Typing)
            //{
            //    //if( temp == "|")
            //    //{
            //    //    textBox1.Text = "";
            //    //    TypeNow = KeyPressType.Wait;
            //    //    label1.Text = "대기중";
            //    //    label1.ForeColor = Color.Green;
            //    //}

            //    if (temp != null)
            //    {
            //        if (temp == "BACK" || temp == "back")
            //        {
            //            textBox1.Text.Remove(textBox1.Text.Length - 1);
            //        }
            //        else
            //            textBox1.Text += temp;

            //    }
            //    else
            //    {
            //        string keyChar = kc.ConvertToString(vkCode);

            //        if (vkCode == 0x08)
            //        {
            //            if (textBox1.Text.Length > 0)
            //                textBox1.Text = textBox1.Text.Remove(textBox1.Text.Length - 1);
            //        }
            //        else
            //        {
            //            AsyncKeyState tempState = GetAsyncKeyState(Keys.LShiftKey) | GetAsyncKeyState(Keys.RShiftKey);
            //            switch (tempState)
            //            {
            //                case AsyncKeyState.HasNotBeenPressed:
            //                    textBox1.Text += keyChar.ToLower();

            //                    break;
            //                case AsyncKeyState.HasBeenPressed:
            //                    break;
            //                case AsyncKeyState.CurrentlyPressed | AsyncKeyState.HasBeenPressed:
            //                    textBox1.Text += keyChar;

            //                    break;
            //                case AsyncKeyState.CurrentlyPressed:
            //                    textBox1.Text += keyChar;

            //                    break;
            //            }
            //        }
            //    }
            //}

            //if (TypeNow == KeyPressType.End)
            //{
            //    //MessageBox.Show(textBox1.Text.Length.ToString());
            //    if (textBox1.Text.Length > 0)
            //    {
            //        string tempText = textBox1.Text;
            //        textBox1.Text = "";
            //        textBox1.Text = trans.englishToKorean(tempText);

            //        irc.SendPublicMessage(textBox1.Text);

            //        textBox1.Text = "";

            //        //if (SetSendMessageTarget())
            //        //{
            //        //    Clipboard.SetText(textBox1.Text);
            //        //    SendMessage(FindWindowExHandle, WM_PASTE, 0, 0);
            //        //    //SendMessage(FindWindowExHandle, 0x0102, 229, 0);
            //        //    SendMessage(FindWindowExHandle, 0x0102, 13, 0);
            //        //}
            //    }
            //    TypeNow = KeyPressType.Wait;
            //}

            return lResult;
        }

        bool SetSendMessageTarget()
        {
            FindWindowHandle = 0;
            FindWindowExHandle = 0;

            FindWindowHandle = FindWindow("mIRC", null);
            //textBox1.Text += FindWindowHandle.ToString();
            if (FindWindowHandle > 0)
                FindWindowExHandle = FindWindowEx(FindWindowHandle, 0, "MDIClient", null);
            //textBox1.Text += "\r\n";
            //textBox1.Text += FindWindowExHandle.ToString();
            if (FindWindowExHandle > 0)
                FindWindowExHandle = FindWindowEx(FindWindowExHandle, 0, "mIRC_Channel", null);
            //textBox1.Text += "\r\n";
            //textBox1.Text += FindWindowExHandle.ToString();
            if (FindWindowExHandle > 0)
            {
                FindWindowExHandle = FindWindowEx(FindWindowExHandle, 0, "RichEdit20A", null);
                return true;
            }
            //textBox1.Text += "\r\n";
            //textBox1.Text += FindWindowExHandle.ToString();

            return false;
        }

        const string _application_name = "kopan_irc v0.3 - kopan84@gmail.com";
        const string _prefix_version = "δ";
        string _gui_version = Application.ProductVersion.ToString();

        string _logpath = "";

        Point _fullmode_point;
        Size  _fullmode_size;
        bool _hold_fullmode = false;

        guiSettings GeneralSettings = new guiSettings("settings-general.xml");

        

        public formTororo()
        {
            InitializeComponent();

            //string temp = trans.englishToKorean("dmaap rlwnrdj");
            //int i = 0;
            

            Properties.Settings.Default.SettingChanging += new System.Configuration.SettingChangingEventHandler(Default_SettingChanging);

            this.ClientSize = Properties.Settings.Default.formTororoSize;

            toolStripNumericUpDownOpacity.NumericUpDownControl.Minimum = 50;
            toolStripNumericUpDownOpacity.NumericUpDownControl.Maximum = 100;
            toolStripNumericUpDownOpacity.NumericUpDownControl.Increment = 2;
          

            decimal opacity = 100;
            decimal interval = 50;
            bool transparency = false;
            bool minimode = true;
            bool scroll_to_end = true;
            string channel = "";
            string nickname = "";

            GeneralSettings.LoadSettings();
            if (!GeneralSettings.IsEmpty())
            {
                opacity = (decimal)GeneralSettings.GetCorrectly("opacity", TypeCode.Decimal, opacity);
                interval = (decimal)GeneralSettings.GetCorrectly("interval", TypeCode.Decimal, interval);
                transparency = (bool)GeneralSettings.GetCorrectly("transparency", TypeCode.Boolean, transparency);
                minimode = (bool)GeneralSettings.GetCorrectly("minimode", TypeCode.Boolean, minimode);
                scroll_to_end = (bool)GeneralSettings.GetCorrectly("scroll_to_end", TypeCode.Boolean, scroll_to_end);
                channel = (string)GeneralSettings.GetCorrectly("channel", TypeCode.String, channel);
                nickname = (string)GeneralSettings.GetCorrectly("nickname", TypeCode.String, nickname);
            }

            toolStripNumericUpDownOpacity.NumericUpDownControl.Value = opacity;
            this.Opacity = (double)toolStripNumericUpDownOpacity.NumericUpDownControl.Value * 0.01;
            
            toolStripCheckTP.Checked = transparency;
            toolStripCheckScroll.Checked = scroll_to_end;

            toolStripTextBoxChannel.Text = channel;
            toolStripTextBoxNick.Text = nickname;


            //rTextBoxOut.Size = this.ClientSize;
            //rTextBoxOut.Height -= toolStrip.Height + textBox1.Height + 10;

            if (Properties.Settings.Default.Dir.Length > 0)
            {
                opf.InitialDirectory = Properties.Settings.Default.Dir;
            }
            else
            {
                opf.InitialDirectory = GetRecentLogPath();
            }
            opf.InitialDirectory = Path.GetDirectoryName(opf.InitialDirectory);

            this.Text = _application_name;

            if (toolStripTextBoxNick.Text == "Guest")
            {
                Random ran = new Random();
                toolStripTextBoxNick.Text += ran.Next(100000).ToString();
            }

            irc = new IRC(this, toolStripTextBoxNick.Text, toolStripTextBoxChannel.Text);
            irc.start();
        }

       

        private void formTororoTest_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
                // ドラッグ中のファイルやディレクトリの取得
                string[] drags = (string[])e.Data.GetData(DataFormats.FileDrop);

                foreach (string d in drags) {
                    if (!System.IO.File.Exists(d)) {
                        // ファイル以外であればイベント・ハンドラを抜ける
                        return;
                    }
                }
                e.Effect = DragDropEffects.Copy;
            }
        }

        

        private void toolStripButtonLoadRecentLog_Click(object sender, EventArgs e)
        {
            irc.start();
        }

        

        private void toolStripButtonStop_CheckedChanged(object sender, EventArgs e)
        {
            
        }

 
        private void toolStripNumericUpDownOpacity_ValueChanged(object sender, EventArgs e)
        {
            this.Opacity = (double)toolStripNumericUpDownOpacity.NumericUpDownControl.Value * 0.01;
        }

        private void formTororo_FormClosing(object sender, FormClosingEventArgs e)
        {
            Properties.Settings.Default.formTororoSize = this.ClientSize; //サイズがおかしくなるのを回避
            Properties.Settings.Default.Save();
            KeyboardHooker.UnHook();
        }

        private void formTororo_FormClosed(object sender, FormClosedEventArgs e)
        {
            GeneralSettings.Set("minimode", toolStripCheckMinimode.Checked);
            GeneralSettings.Set("scroll_to_end", toolStripCheckScroll.Checked);
            GeneralSettings.Set("transparency", toolStripCheckTP.Checked);
            GeneralSettings.Set("opacity", toolStripNumericUpDownOpacity.NumericUpDownControl.Value);
            GeneralSettings.Set("channel", toolStripTextBoxChannel.Text);
            GeneralSettings.Set("nickname", toolStripTextBoxNick.Text);

            GeneralSettings.Save();
            
            if (!String.IsNullOrEmpty(_logpath))
            {
                Properties.Settings.Default.Dir = Path.GetDirectoryName(_logpath);
            }

            irc.end();
        }

        private void toolStripCheckTP_CheckedChanged(object sender, EventArgs e)
        {
            if (toolStripCheckTP.Checked) {
                this.TransparencyKey = rTextBoxOut.BackColor;
            } else {
                this.TransparencyKey = new Color();
            }
        }


        private void formTororo_Activated(object sender, EventArgs e)
        {
            textBox1.Select();
            SetGUIMode(true);
            //ここにスクロール位置を戻す処理を入れたらいいかも？
        }

        private void formTororo_Load(object sender, EventArgs e)
        {
            KeyboardHooker.SetHookName(toolStripTextBoxHookName.Text);
            //3. 후크 이벤트를 연결한다.
            HookedKeyboardNofity += new KeyboardHooker.HookedKeyboardUserEventHandler(Form1_HookedKeyboardNofity);

            //4. 자동으로 훅을 시작한다. 여기서 훅에 의한 이벤트를 연결시킨다.
            KeyboardHooker.Hook(HookedKeyboardNofity);
        }

        private void formTororo_Deactivate(object sender, EventArgs e)
        {
            SetGUIMode(false);
        }

        void Default_SettingChanging(object sender, System.Configuration.SettingChangingEventArgs e)
        {
            if (this.WindowState != FormWindowState.Normal)
            {
                if ((e.SettingName == "formTororoSize") || (e.SettingName == "formTororoLocation"))
                {
                    e.Cancel = true;
                }
            }
        }

        

       

        // versions: {prefix, core, gui}
        private string GetVersionString(string app_name, params string[] versions)
        {
            string prefix = versions[0];
            string core = versions[1];
            string gui = versions[2];
            string output = app_name;

            if (!String.IsNullOrEmpty(prefix))
            {
                output += " " + prefix;
            }
            if (!String.IsNullOrEmpty(core))
            {
                output += " c:" + core;
            }
            if (!String.IsNullOrEmpty(gui))
            {
                output += " g:" + gui;
            }
            return output;
        }

        private string GetRecentLogPath()
        {
            string recent_file_path = "";
            string[] file_path_list;
            DateTime recent_date = DateTime.MinValue;
            file_path_list = System.IO.Directory.GetFiles(
                System.Environment.GetFolderPath(Environment.SpecialFolder.Personal)
                , "*.*");
            if (file_path_list == null) return null;
            foreach (string file_path in file_path_list)
            {
                DateTime temp_date;
                temp_date = System.IO.File.GetCreationTime(file_path);
                if (temp_date > recent_date)
                {
                    recent_date = temp_date;
                    recent_file_path = file_path;
                }
            }
            return recent_file_path;
        }



        

        /// <summary>
        /// RichTextBoxの末尾にRTFを追加
        /// </summary>
        /// <param name="rtb0">RTFを追加するRichTextBox</param>
        /// <param name="rtb1">追加するRichTextBox</param>
        public static void AppendRtfToRichTextBox(
            RichTextBox rtb0, RichTextBox rtb1)
        {
            rtb0.SelectionStart = rtb0.TextLength;
            rtb0.SelectedRtf = rtb1.Rtf;
        }

      

        private void SetGUIMode(bool full)
        {

            if (this.WindowState.Equals(FormWindowState.Minimized) || _hold_fullmode) return;
            this.SuspendLayout();
            Point minimode_point = PointToScreen(listBoxUserList.Bounds.Location);
            switch (this.FormBorderStyle)
            {
                case FormBorderStyle.None:

                    break;
                case FormBorderStyle.Sizable:
                    _fullmode_point = this.Location;
                    _fullmode_size = this.ClientSize;
                    break;
            }

            if (full)
            {
                this.FormBorderStyle = FormBorderStyle.Sizable;
                this.Location = _fullmode_point;
                this.ClientSize = _fullmode_size;
                listBoxUserList.Top = toolStrip.Height;
                listBoxUserList.Left = toolStrip.Left;
                rTextBoxOut.SelectionLength = 0;
                rTextBoxOut.Top = listBoxUserList.Top;
                rTextBoxOut.Left = listBoxUserList.Right;
                //textBox1.Top = toolStrip.Height;
            }
            else if (toolStripCheckMinimode.Checked)
            {
                this.FormBorderStyle = FormBorderStyle.None;
                this.Location = minimode_point;
                //this.ClientSize = rTextBoxOut.Size;
                this.ClientSize = new Size(rTextBoxOut.Size.Width + listBoxUserList.Size.Width, rTextBoxOut.Size.Height);
                //Size tempSize = new Size();
                //tempSize.Width = rTextBoxOut.Size.Width;
                //tempSize.Height = rTextBoxOut.Size.Height + textBox1.Height + 10;
                //this.ClientSize = tempSize;
                //this.ClientSize.Height = 
                // スクロールバーを隠す
                // 強力透過の場合は半分だけ隠す
                if (toolStripCheckTP.Checked)
                {
                    this.Width -= SystemInformation.VerticalScrollBarWidth / 2;
                }
                else
                {
                    this.Width -= SystemInformation.VerticalScrollBarWidth;
                }
                listBoxUserList.Location = toolStrip.Location;
                rTextBoxOut.Location = new Point(listBoxUserList.Location.X + listBoxUserList.Width,listBoxUserList.Location.Y);
                //rTextBoxOut.Location = listBoxUserList.Location;
                //textBox1.Top = rTextBoxOut.Top;
            }
            this.ResumeLayout();
        }

        // ------------



        public class KeyboardHooker
        {
            const int WM_KEYDOWN = 0x100;
            const int WM_KEYUP = 0x101;
            const int WM_SYSKEYDOWN = 0x104;
            const int WM_SYSKEYUP = 0x105;

            // 후킹된 키보드 이벤트를 처리할 이벤트 핸들러
            private delegate long HookedKeyboardEventHandler(int nCode, int wParam, IntPtr lParam);

            /// <summary>
            /// 유저에게 노출할 이벤트 핸들러
            /// iKeyWhatHappened : 현재 입력이 KeyDown/KeyUp인지 여부 - Key별로 숫자가 다르다.
            /// vkCode : virtual key 값, System.Windows.Forms.Key의 상수 값을 int로 변환해서 대응시키면 된다.
            /// </summary>
            /// <param name="iKeyWhatHappened"></param>
            /// <param name="bAlt"></param>
            /// <param name="bCtrl"></param>
            /// <param name="bShift"></param>
            /// <param name="bWindowKey"></param>
            /// <param name="vkCode"></param>
            /// <returns></returns>
            public delegate long HookedKeyboardUserEventHandler(int iKeyWhatHappened, int vkCode);

            // 후킹된 모듈의 핸들. 후킹이 성공했는지 여부를 식별하기 위해서 사용
            private const int WH_KEYBOARD_LL = 13;		// Intalls a hook procedure that monitors low-level keyboard input events.
            private static long m_hDllKbdHook;
            private static KBDLLHOOKSTRUCT m_KbDllHs = new KBDLLHOOKSTRUCT();
            private static IntPtr m_LastWindowHWnd;
            public static IntPtr m_CurrentWindowHWnd;
            static string HookName;
            static bool GlobalHook = false;

            public static void SetHookName(string name)
            {
                HookName = name;
            }

            public static void SetGlobalHook(bool _Value)
            {
                if (_Value)
                {
                    GlobalHook = true;
                }
                else
                {
                    GlobalHook = false;
                }
            }

            static bool HookCheck()
            {
                if (GlobalHook)
                {
                    return true;
                }
                else
                {
                    return GetTopWindowText() == HookName;
                }
            }

            // 후킹한 메시지를 받을 이벤트 핸들러
            private static HookedKeyboardEventHandler m_LlKbEh = new HookedKeyboardEventHandler(HookedKeyboardProc);

            // 콜백해줄 이벤트 핸들러 ; 사용자측에 이벤트를 넘겨주기 위해서 사용
            private static HookedKeyboardUserEventHandler m_fpCallbkProc = null;



            #region KBDLLHOOKSTRUCT Documentation
            /// <summary>
            /// The KBDLLHOOKSTRUCT structure contains information about a low-level keyboard input event. 
            /// </summary>
            /// <remarks>
            /// <para>
            /// See <a href="ms-help://MS.VSCC/MS.MSDNVS/winui/hooks_0cxe.htm">KBDLLHOOKSTRUCT</a><BR/>
            /// </para>
            /// <para>
            /// <code>
            /// [C++]
            /// typedef struct KBDLLHOOKSTRUCT {
            ///     DWORD     vkCode;
            ///     DWORD     scanCode;
            ///     DWORD     flags;
            ///     DWORD     time;
            ///     ULONG_PTR dwExtraInfo;
            ///     ) KBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT;
            /// </code>
            /// </para>
            /// </remarks>
            #endregion
            private struct KBDLLHOOKSTRUCT
            {
                #region vkCode
                /// <summary>
                /// Specifies a virtual-key code. The code must be a value in the range 1 to 254. 
                /// </summary>
                #endregion
                public int vkCode;
                #region scanCode
                /// <summary>
                /// Specifies a hardware scan code for the key. 
                /// </summary>
                #endregion
                public int scanCode;
                #region flags
                /// <summary>
                /// Specifies the extended-key flag, event-injected flag, context code, and transition-state flag.
                /// </summary>
                /// <remarks>
                /// For valid flag values and additional information, see <a href="ms-help://MS.VSCC/MS.MSDNVS/winui/hooks_0cxe.htm">MSDN Documentation for KBDLLHOOKSTRUCT</a>
                /// </remarks>
                #endregion
                public int flags;
                #region time
                /// <summary>
                /// Specifies the time stamp for this message. 
                /// </summary>
                #endregion
                public int time;
                #region dwExtraInfo
                /// <summary>
                /// Specifies extra information associated with the message. 
                /// </summary>
                #endregion
                public IntPtr dwExtraInfo;

                #region ToString()
                /// <summary>
                /// Creates a string representing the values of all the variables of an instance of this structure.
                /// </summary>
                /// <returns>A string</returns>
                #endregion
                public override string ToString()
                {
                    string temp = "KBDLLHOOKSTRUCT\r\n";
                    temp += "vkCode: " + vkCode.ToString() + "\r\n";
                    temp += "scanCode: " + scanCode.ToString() + "\r\n";
                    temp += "flags: " + flags.ToString() + "\r\n";
                    temp += "time: " + time.ToString() + "\r\n";
                    return temp;
                }
            }//end of structure

            #region CopyMemory Documentation
            /// <summary>
            /// The CopyMemory function copies a block of memory from one location to another. 
            /// </summary>
            /// <remarks>
            /// <para>
            /// See <a href="ms-help://MS.VSCC/MS.MSDNVS/memory/memman_0z95.htm">CopyMemory</a><BR/>
            /// </para>
            /// <para>
            /// <code>
            /// [C++]
            /// VOID CopyMemory(
            ///		PVOID Destination,   // copy destination
            ///		CONST VOID* Source,  // memory block
            ///		SIZE_T Length        // size of memory block
            ///		);
            /// </code>
            /// </para>
            /// </remarks>
            #endregion
            [DllImport(@"kernel32.dll", CharSet = CharSet.Auto)]
            private static extern void CopyMemory(ref KBDLLHOOKSTRUCT pDest, IntPtr pSource, long cb);

            #region GetForegroundWindow Documentation
            /// <summary>
            /// The GetForegroundWindow function returns a handle to the foreground window (the window with which the user is currently working).
            /// The system assigns a slightly higher priority to the thread that creates the foreground window than it does to other threads. 
            /// </summary>
            /// <remarks>
            /// <para>
            /// See <a href="ms-help://MS.VSCC/MS.MSDNVS/winui/windows_4f5j.htm">GetForegroundWindow</a><BR/>
            /// </para>
            /// <para>
            /// <code>
            /// [C++]
            /// HWND GetForegroundWindow(VOID);
            /// </code>
            /// </para>
            /// </remarks>
            #endregion
            [DllImport(@"user32.dll", CharSet = CharSet.Auto)]
            private static extern IntPtr GetForegroundWindow();

            [DllImport("user32.dll")]
            static extern int GetWindowTextLength(IntPtr hWnd);

            //  int GetWindowText(
            //      __in   HWND hWnd,
            //      __out  LPTSTR lpString,
            //      __in   int nMaxCount
            //  );
            [DllImport("user32.dll")]
            private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

            //  DWORD GetWindowThreadProcessId(
            //      __in   HWND hWnd,
            //      __out  LPDWORD lpdwProcessId
            //  );
            [DllImport("user32.dll")]
            private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

            //HANDLE WINAPI OpenProcess(
            //  __in  DWORD dwDesiredAccess,
            //  __in  BOOL bInheritHandle,
            //  __in  DWORD dwProcessId
            //);
            [DllImport("kernel32.dll")]
            private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);

            [DllImport("kernel32.dll")]
            private static extern bool CloseHandle(IntPtr handle);

            //  DWORD WINAPI GetModuleBaseName(
            //      __in      HANDLE hProcess,
            //      __in_opt  HMODULE hModule,
            //      __out     LPTSTR lpBaseName,
            //      __in      DWORD nSize
            //  );
            [DllImport("psapi.dll")]
            private static extern uint GetModuleBaseName(IntPtr hWnd, IntPtr hModule, StringBuilder lpFileName, int nSize);

            //  DWORD WINAPI GetModuleFileNameEx(
            //      __in      HANDLE hProcess,
            //      __in_opt  HMODULE hModule,
            //      __out     LPTSTR lpFilename,
            //      __in      DWORD nSize
            //  );
            [DllImport("psapi.dll")]
            private static extern uint GetModuleFileNameEx(IntPtr hWnd, IntPtr hModule, StringBuilder lpFileName, int nSize);

            public static string GetTopWindowText()
            {
                IntPtr hWnd = GetForegroundWindow();
                int length = GetWindowTextLength(hWnd);
                StringBuilder text = new StringBuilder(length + 1);
                GetWindowText(hWnd, text, text.Capacity);
                return text.ToString();
            }

            public static string GetTopWindowName()
            {
                IntPtr hWnd = GetForegroundWindow();
                uint lpdwProcessId;
                GetWindowThreadProcessId(hWnd, out lpdwProcessId);

                IntPtr hProcess = OpenProcess(0x0410, false, lpdwProcessId);

                StringBuilder text = new StringBuilder(1000);
                //GetModuleBaseName(hProcess, IntPtr.Zero, text, text.Capacity);
                GetModuleFileNameEx(hProcess, IntPtr.Zero, text, text.Capacity);

                CloseHandle(hProcess);

                return text.ToString();
            }

            #region GetAsyncKeyState
            /// <summary>
            /// The GetAsyncKeyState function determines whether a key is up or down at the time the function is called,
            /// and whether the key was pressed after a previous call to GetAsyncKeyState. 
            /// </summary>
            /// <remarks>
            /// <para>
            /// See <a href="ms-help://MS.VSCC/MS.MSDNVS/winui/keybinpt_1x0l.htm">GetAsyncKeyState</a><BR/>
            /// </para>
            /// <para>
            /// <code>
            /// [C++]
            ///	SHORT GetAsyncKeyState(
            ///		int vKey   // virtual-key code
            ///		);
            /// </code>
            /// </para>
            /// </remarks>
            #endregion
            [DllImport(@"user32.dll", CharSet = CharSet.Auto)]
            private static extern uint GetAsyncKeyState(int vKey);

            #region CallNextHookEx Documentation
            /// <summary>
            /// The CallNextHookEx function passes the hook information to the next hook procedure in the current hook chain.
            /// A hook procedure can call this function either before or after processing the hook information. 
            /// </summary>
            /// <remarks>
            /// <para>
            /// See <a href="ms-help://MS.VSCC/MS.MSDNVS/winui/hooks_57aw.htm">CallNextHookEx</a><BR/>
            /// </para>
            /// <para>
            /// <code>
            /// [C++]
            /// LRESULT CallNextHookEx(
            ///    HHOOK hhk,      // handle to current hook
            ///    int nCode,      // hook code passed to hook procedure
            ///    WPARAM wParam,  // value passed to hook procedure
            ///    LPARAM lParam   // value passed to hook procedure
            ///    );
            /// </code>
            /// </para>
            /// </remarks>
            #endregion
            [DllImport(@"user32.dll", CharSet = CharSet.Auto)]
            private static extern long CallNextHookEx(long hHook, long nCode, long wParam, IntPtr lParam);

            #region SetWindowsHookEx Documentation
            /// <summary>
            /// The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain.
            /// You would install a hook procedure to monitor the system for certain types of events.
            /// These events are associated either with a specific thread or with all threads in the same
            /// desktop as the calling thread. 
            /// </summary>
            /// <remarks>
            /// <para>
            /// See <a href="ms-help://MS.VSCC/MS.MSDNVS/winui/hooks_7vaw.htm">SetWindowsHookEx</a><BR/>
            /// </para>
            /// <para>
            /// <code>
            /// [C++]
            ///  HHOOK SetWindowsHookEx(
            ///		int idHook,        // hook type
            ///		HOOKPROC lpfn,     // hook procedure
            ///		HINSTANCE hMod,    // handle to application instance
            ///		DWORD dwThreadId   // thread identifier
            ///		);
            /// </code>
            /// </para>
            /// </remarks>
            #endregion
            [DllImport(@"user32.dll", CharSet = CharSet.Auto)]
            private static extern long SetWindowsHookEx(int idHook, HookedKeyboardEventHandler lpfn, long hmod, int dwThreadId);

            #region UnhookWindowsEx Documentation
            /// <summary>
            /// The UnhookWindowsHookEx function removes a hook procedure installed in a hook chain by the SetWindowsHookEx function. 
            /// </summary>
            /// <remarks>
            /// <para>
            /// See <a href="ms-help://MS.VSCC/MS.MSDNVS/winui/hooks_6fy0.htm">UnhookWindowsHookEx</a><BR/>
            /// </para>
            /// <para>
            /// <code>
            /// [C++]
            /// BOOL UnhookWindowsHookEx(
            ///    HHOOK hhk   // handle to hook procedure
            ///    );
            /// </code>
            /// </para>
            /// </remarks>
            #endregion
            [DllImport(@"user32.dll", CharSet = CharSet.Auto)]
            private static extern long UnhookWindowsHookEx(long hHook);

            // declare the following Win32 API calls 
            [DllImport("user32.dll")]
            public unsafe static extern int ToAscii(int VirtualKey, int ScanCode,
            byte* lpKeyState, ref uint lpChar, int uFlags);
            [DllImport("user32.dll")]
            public unsafe static extern int GetKeyboardState(byte* lpKeyState);
            unsafe protected char getAsciiCharacter(int iKeyCode, int iFlags)
            {
                uint lpChar = 0;
                byte[] bCharData = new byte[256];
                fixed (byte* pByte = bCharData)
                {
                    GetKeyboardState(pByte);
                    ToAscii(iKeyCode, iFlags, pByte, ref lpChar, 0);
                }
                return (char)lpChar;
            }

            // Valid return for nCode parameter of LowLevelKeyboardProc
            private const int HC_ACTION = 0;
            private static long HookedKeyboardProc(int nCode, int wParam, IntPtr lParam)
            {
                //System.Diagnostics.Debug.WriteLine(lParam.ToInt64().ToString());
                long lResult = 0;


                if (nCode == HC_ACTION && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && HookCheck()) //LowLevelKeyboardProc
                {
                    //visusl studio 2008 express 버전에서는 빌드 옵션에서 안전하지 않은 코드 허용에 체크
                    unsafe
                    {
                        //도대체 어디서 뭘 카피해놓는다는건지 이거 원..
                        CopyMemory(ref m_KbDllHs, lParam, sizeof(KBDLLHOOKSTRUCT));
                    }

                    //전역 후킹을 하기 위해서 현재 활성화 된 윈도우의 핸들값을 찾는다.
                    //그래서 이 윈도우에서 발생하는 이벤트를 후킹해야 전역후킹이 가능해진다.
                    m_CurrentWindowHWnd = GetForegroundWindow();

                    //후킹하려는 윈도우의 핸들을 방금 찾아낸 핸들로 바꾼다.
                    if (m_CurrentWindowHWnd != m_LastWindowHWnd)
                        m_LastWindowHWnd = m_CurrentWindowHWnd;

                    // 이벤트 발생
                    if (m_fpCallbkProc != null)
                    {
                        lResult = m_fpCallbkProc(m_KbDllHs.flags, m_KbDllHs.vkCode);
                    }


                }
                else if (nCode < 0) //나머지는 그냥 통과시킨다.
                {
                    #region MSDN Documentation on return conditions
                    // "If nCode is less than zero, the hook procedure must pass the message to the 
                    // CallNextHookEx function without further processing and should return the value 
                    // returned by CallNextHookEx. "
                    // ...
                    // "If nCode is greater than or equal to zero, and the hook procedure did not 
                    // process the message, it is highly recommended that you call CallNextHookEx 
                    // and return the value it returns;"
                    #endregion
                    return CallNextHookEx(m_hDllKbdHook, nCode, wParam, lParam);
                }

                //
                //lResult 값이 0이면 후킹 후 이벤트를 시스템으로 흘려보내고
                //0이 아니면 후킹도 하고 이벤트도 시스템으로 보내지 않는다.
                return lResult;
            }

            // 후킹 시작
            public static bool Hook(HookedKeyboardUserEventHandler callBackEventHandler)
            {
                bool bResult = true;
                m_hDllKbdHook = SetWindowsHookEx(
                    (int)WH_KEYBOARD_LL,
                    m_LlKbEh,
                    Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]).ToInt32(),
                    0);

                if (m_hDllKbdHook == 0)
                {
                    bResult = false;
                }
                // 외부에서 KeyboardHooker의 이벤트를 받을 수 있도록 이벤트 핸들러를 할당함
                KeyboardHooker.m_fpCallbkProc = callBackEventHandler;

                return bResult;
            }

            // 후킹 중지
            public static void UnHook()
            {
                //프로그램 종료 시점에서 호출해주자.
                UnhookWindowsHookEx(m_hDllKbdHook);
            }

        }

       

        private void toolStripButtonChannel_Click(object sender, EventArgs e)
        {
            irc.JoinChannel(toolStripTextBoxChannel.Text);
        }

        private void toolStripButtonNick_Click(object sender, EventArgs e)
        {
            irc.ChangeNick(toolStripTextBoxNick.Text);
        }

        private void toolStripButtonFont_Click(object sender, EventArgs e)
        {
            DialogResult dr = this.fontDialog1.ShowDialog();
            //확인버튼 누르면 변경
            if (dr == DialogResult.OK)
            {
                rTextBoxOut.Font = fontDialog1.Font;
            }
        }

        private void toolStripTextBoxHookName_TextChanged(object sender, EventArgs e)
        {
            KeyboardHooker.SetHookName(toolStripTextBoxHookName.Text);
        }

        private void textBox1_KeyDown(object sender, KeyEventArgs e)
        {
            if(e.KeyCode == Keys.Enter)
            {
                if (textBox1.Text.Length > 0)
                {
                    irc.SendPublicMessage(textBox1.Text);
                    textBox1.Text = "";
                }

                ShowWindow(BeforeHandle, 1);
                BringWindowToTop(BeforeHandle);
                SetForegroundWindow(BeforeHandle);
            }
            else if(e.KeyCode == Keys.Escape)
            {
                textBox1.Text = "";
                ShowWindow(BeforeHandle, 1);
                BringWindowToTop(BeforeHandle);
                SetForegroundWindow(BeforeHandle);
            }
        }

        private void toolstripCheckTopMost_Changed(object sender, EventArgs e)
        {
            if(toolStripCheckTopMost.Checked)
            {
                this.TopMost = true;
            }
            else
            {
                this.TopMost = false;
            }
        }

        private void toolStripCheckGlobalHook_Changed(object sender, EventArgs e)
        {
            if (toolStripCheckGlobalHook.Checked)
            {
                KeyboardHooker.SetGlobalHook(true);
            }
            else
            {
                KeyboardHooker.SetGlobalHook(false);
            }

        }

        private void rTextBoxOut_LinkClicked(object sender, LinkClickedEventArgs e)
        {
            System.Diagnostics.Process.Start(e.LinkText);
        }



    }
}
